package matching.xml;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import matching.ClassMatch;
import matching.triple.ClassTriple;
import matching.DataPropertyMatch;
import matching.triple.DataPropertyTriple;
import matching.Match;
import matching.triple.ObjectPropertyTriple;
import matching.Restriction;
import matching.Matching;
import matching.ObjectPropertyMatch;
import matching.path.PairOfPath;
import matching.path.Path;
import matching.path.PropertyPair;
import ontology.Ontology;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;
import org.semanticweb.owl.apibinding.OWLManager;
import org.semanticweb.owl.model.OWLClass;
import org.semanticweb.owl.model.OWLDataProperty;
import org.semanticweb.owl.model.OWLObjectProperty;
import org.semanticweb.owl.model.OWLOntologyCreationException;
import org.semanticweb.owl.model.OWLOntologyManager;


public class MatchingManager {

    private static MatchingManager instance;
    public SAXBuilder sb;
    public Matching matching;
    public Document matchingDoc;
    public Ontology srcOnto;
    public Ontology targOnto;
    public String src_ontoPath;
    public String targ_ontoPath;
    public File matchingXml;
    public OWLOntologyManager manager = OWLManager.createOWLOntologyManager();

    private MatchingManager() {
        sb = new SAXBuilder();
    }

    public static MatchingManager getInstance(){
        if(instance==null){
            instance = new MatchingManager();
        }
        return  instance;
    }

    public void initMatching(String src_onto, String targ_onto) throws OWLOntologyCreationException{
        setSourceOntology(src_onto);
        setTargetOntology(targ_onto);
        matching = new Matching(srcOnto, targOnto, src_onto, targ_onto);
        matchingXml = new File("matching.xml");
        matchingDoc = null;
            
    }

    public void loadFile(File file) throws JDOMException, IOException, URISyntaxException, OWLOntologyCreationException{
        matchingXml = file;
        matchingDoc = sb.build(file);
        loadMatchingXML();
    }

    private void setSourceOntology(String src_onto) throws OWLOntologyCreationException{
         URI uriSrc = URI.create(src_onto);
         srcOnto = new Ontology(uriSrc);
         src_ontoPath = src_onto;
    }

   private void setTargetOntology(String targ_onto) throws OWLOntologyCreationException{
        URI uriTarg = URI.create(targ_onto);
        targOnto = new Ontology(uriTarg);
        targ_ontoPath = targ_onto;
    }
    
    private void loadMatchingXML() throws OWLOntologyCreationException, URISyntaxException{
      
        // elemento raiz do arquivo de matching xml
        Element vocMat = matchingDoc.getRootElement();
        // atribui as string a uri das ontologias fonte e alvo
        setSourceOntology(vocMat.getAttributeValue(AttributesMatching.sourceLocationAttr));
        setTargetOntology(vocMat.getAttributeValue(AttributesMatching.targetLocationAttr));
        matching = new Matching(srcOnto, targOnto, src_ontoPath, targ_ontoPath);
        //Recuperamos os elementos filhos (children)
        List elements = vocMat.getChildren();
        Iterator i = elements.iterator();
        Element element;
        //Iteramos com os elementos filhos, e filhos do dos filhos
        String type;
        while (i.hasNext()) {
            element = (Element) i.next();
            type = element.getAttributeValue("type", vocMat.getNamespace("xsi"));
          if(type.equals(MatchTypes.dataPropMatch)){
                loadDataPropertyMatch(element, srcOnto, targOnto);
          }else if(type.equals(MatchTypes.objPropMatch)){
                loadObjPropertyMatch(element, srcOnto, targOnto);
          }else if(type.equals(MatchTypes.classMatch)){
                loadClassMatch(element, srcOnto, targOnto);
          }
        }        
    }

    private void loadDataPropertyMatch(Element e, Ontology src, Ontology tgt) throws URISyntaxException{
        Element srcTrpElem = e.getChild(MatchElements.source);
        Element tgtTrpElem = e.getChild(MatchElements.target);
        // propriedade de dados: elementos no matching
        OWLDataProperty elemSrc = src.getDataProperty(srcTrpElem.getChildText(MatchElements.element));
	OWLDataProperty elemTgt = tgt.getDataProperty(tgtTrpElem.getChildText(MatchElements.element));
        // classes: contexto
        OWLClass contSrc = src.getClass(srcTrpElem.getChildText(MatchElements.context));
	OWLClass contTgt = tgt.getClass(tgtTrpElem.getChildText(MatchElements.context));

        if(elemSrc==null||elemTgt==null||contSrc==null||contTgt==null){
             System.out.println("dados invalidos: "+srcTrpElem.getChildText(MatchElements.element)+
                    " "+tgtTrpElem.getChildText(MatchElements.element));
            return;
        }

        Element rsSrc = srcTrpElem.getChild(MatchElements.restriction);
        Element rsTgt = tgtTrpElem.getChild(MatchElements.restriction);
        
        Restriction resSrc=null;
        Restriction resTgt=null;

        if(rsSrc!=null) resSrc = new Restriction(src.getDataProperty(rsSrc.getChildText(MatchElements.attribute)),
                    rsSrc.getChildText(MatchElements.value));
        if(rsTgt!=null) resTgt = new Restriction(tgt.getDataProperty(rsTgt.getChildText(MatchElements.attribute)),
                    rsTgt.getChildText(MatchElements.value));        

        DataPropertyTriple srcT = new DataPropertyTriple(elemSrc, contSrc, resSrc, srcOnto);
        DataPropertyTriple tgtT =  new DataPropertyTriple(elemTgt, contTgt, resTgt, targOnto);
        
        try {
            DataPropertyMatch match = new DataPropertyMatch(srcT, tgtT, srcOnto, targOnto);
            matching.addMatching(match);
            Element pElem;
            if((pElem=srcTrpElem.getChild("path"))!=null){               
                srcT.setPath(loadPath(pElem, srcOnto));
            }
            if((pElem=tgtTrpElem.getChild("path"))!=null){
                tgtT.setPath(loadPath(pElem, targOnto));
            }
            
        } catch (Exception ex) {
            Logger.getLogger(MatchingManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private Path loadPath(Element pElem, Ontology onto){
        Path p = new Path(onto.owl,onto.getClass(pElem.getChild("class").getText()));
        for (Object node : pElem.getChild("nodes").getChildren("node")) {
            Element elN = (Element)node;
            p.addNode(new PropertyPair(onto.getClass(elN.getChild("class").getText()),
                    onto.getObjectProperty(elN.getChild("property").getText())));
        }
        return p;
    }

    private void loadObjPropertyMatch(Element e, Ontology src, Ontology tgt) {
        Element srcTrpElem = e.getChild(MatchElements.source);
        Element tgtTrpElem = e.getChild(MatchElements.target);
        // propriedade de dados: elementos no matching
        OWLObjectProperty elemSrc = src.getObjectProperty(srcTrpElem.getChildText(MatchElements.element));
       	OWLObjectProperty elemTgt = tgt.getObjectProperty(tgtTrpElem.getChildText(MatchElements.element));
        // classes: contexto
        OWLClass contSrc = src.getClass(srcTrpElem.getChildText(MatchElements.context));
	OWLClass contTgt = tgt.getClass(tgtTrpElem.getChildText(MatchElements.context));
        
        if(elemSrc==null||elemTgt==null||contSrc==null||contTgt==null){
            System.out.println("dados invalidos: "+srcTrpElem.getChildText(MatchElements.element)+
                    " "+tgtTrpElem.getChildText(MatchElements.element));
            return;
        }

        Element rsSrc = srcTrpElem.getChild(MatchElements.restriction);
        Element rsTgt = tgtTrpElem.getChild(MatchElements.restriction);
        
        Restriction resSrc=null;
        Restriction resTgt=null;

        if(rsSrc!=null) resSrc = new Restriction(src.getDataProperty(rsSrc.getChildText(MatchElements.attribute)),
                    rsSrc.getChildText(MatchElements.value));
        if(rsTgt!=null) resTgt = new Restriction(tgt.getDataProperty(rsTgt.getChildText(MatchElements.attribute)),
                    rsTgt.getChildText(MatchElements.value));

        ObjectPropertyTriple srcT = new ObjectPropertyTriple(elemSrc, contSrc, resSrc, srcOnto);
        ObjectPropertyTriple tgtT =  new ObjectPropertyTriple(elemTgt, contTgt, resTgt, targOnto);

        try {
            matching.addMatching(new ObjectPropertyMatch(srcT, tgtT, srcOnto, targOnto));
            Element pElem;
            if((pElem=srcTrpElem.getChild("path"))!=null){
                srcT.setPath(loadPath(pElem, srcOnto));
            }
            if((pElem=tgtTrpElem.getChild("path"))!=null){
                tgtT.setPath(loadPath(pElem, targOnto));
            }
        } catch (Exception ex) {
            Logger.getLogger(MatchingManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void loadClassMatch(Element e, Ontology src, Ontology tgt) {
        Element srcTrpElem = e.getChild(MatchElements.source);
        Element tgtTrpElem = e.getChild(MatchElements.target);
        // propriedade de dados: elementos no matching
        OWLClass elemSrc = src.getClass(srcTrpElem.getChildText(MatchElements.element));
	OWLClass elemTgt = tgt.getClass(tgtTrpElem.getChildText(MatchElements.element));

        if(elemSrc==null||elemTgt==null){
            System.out.println("dados invalidos: "+srcTrpElem.getChildText(MatchElements.element)+" "+tgtTrpElem.getChildText(MatchElements.element));
            return;
        }

        Element rsSrc = srcTrpElem.getChild(MatchElements.restriction);
        Element rsTgt = tgtTrpElem.getChild(MatchElements.restriction);
        
        Restriction resSrc=null;
        Restriction resTgt=null;

        if(rsSrc!=null) resSrc = new Restriction(src.getDataProperty(rsSrc.getChild(MatchElements.attribute).getText()),
                    rsSrc.getChild(MatchElements.value).getText());
        if(rsTgt!=null) resTgt = new Restriction(tgt.getDataProperty(rsTgt.getChildText(MatchElements.attribute)),
                    rsTgt.getChildText(MatchElements.value));

        ClassTriple srcT = new ClassTriple(elemSrc,resSrc, srcOnto);
        ClassTriple tgtT = new ClassTriple(elemTgt,resTgt, targOnto);

        try {
            matching.addMatching(new ClassMatch(srcT, tgtT, srcOnto, targOnto));
        } catch (Exception ex) {
            Logger.getLogger(MatchingManager.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    public void addMatch(Match m){
        matching.matchings.add(m);
    }

    public void saveMatching() throws IOException{
       if(matchingXml != null && matching != null){
            Document doc = new Document();
            doc.setRootElement(matching.toElementXml());
            Format fmt = Format.getPrettyFormat();
            fmt.setIndent( "    " );
            FileWriter arquivo = new FileWriter(matchingXml);
            XMLOutputter xout = new XMLOutputter(fmt);
            xout.output(doc, arquivo);
            arquivo.close();
        }
    }

   




}
